package cn.siat.gene.service;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import cn.siat.common.security.utils.SecurityUtils;
import cn.siat.gene.domain.DesignTBExternalPaccout;
import cn.siat.gene.mapper.DesignTBExternalPaccoutMapper;
import org.springframework.http.HttpStatus;
import cn.siat.common.core.exception.ServiceException;
import cn.siat.common.redis.service.RedisService;
import cn.siat.gene.domain.GeneDataFileVo;
import cn.siat.gene.domain.GeneDataVo;
import cn.siat.gene.mapper.ProgrammeMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;


/**
 * 基础数据库系统关联
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GenePoolService {

    private final RedisService redisService;
    private final RestTemplate restTemplate;
    private final ProgrammeMapper programmeMapper;
    private final SequenceService sequenceService;
    private final DesignTBExternalPaccoutMapper designTBExternalPaccoutMapper;
    @Value("${genePool.url}")
    private  String url;

    @Value("${path.sequence}")
    private String sequencePath;


    /**
     * 账号验证
     * @param username
     * @param password
     * @return
     */
//    public JSONObject login(String username, String password) {
//        JSONObject entity = restTemplate.getForObject(url+"api/captcha/create", JSONObject.class);
//        if(entity.getInteger("code") != HttpStatus.OK.value()){
//            throw new ServiceException(entity.getString("msg"));
//        }
//        JSONObject codeData = entity.getJSONObject("data");
//        //登陆参数
//        MultiValueMap<String, String> loginParams = new LinkedMultiValueMap<>();
//        loginParams.add("username", username);
//        loginParams.add("password", password);
//        loginParams.add("captchaKey", codeData.getString("key"));
//        loginParams.add("captchaCode", codeData.getString("code"));
//        JSONObject loginResponse = restTemplate.postForObject(
////                url+"api/login",
//                url+"api/getUserToken",
//                loginParams,
//                JSONObject.class);
//        if(loginResponse.getInteger("code")== HttpStatus.OK.value()){
//            String verifyKey = "gene_pool_token:" + SecurityUtils.getUserId();
//            redisService.deleteObject(verifyKey);
//        }
//        return loginResponse;
//    }
    public JSONObject login(String username, String password) {
        String getTokenUrl = url+"api/getUserToken?username="+username+"&password="+password;
        JSONObject loginResponse = restTemplate.getForObject(
                getTokenUrl,
                JSONObject.class);
        if(loginResponse.getInteger("code")== HttpStatus.OK.value()){
            String verifyKey = "gene_pool_token:" + SecurityUtils.getUserId();
            redisService.deleteObject(verifyKey);
        }
        return loginResponse;
    }


    /**
     * 获取绑定账号
     * @return
     */
    private DesignTBExternalPaccout getAccount(){
        QueryWrapper<DesignTBExternalPaccout> queryWrapper= new QueryWrapper<>();
        queryWrapper.eq("external_system_name", "基础数据库系统")
                .eq("is_delete", 1)
                .eq("is_bind", 1)
                .eq("user_id", SecurityUtils.getUserId());
        DesignTBExternalPaccout one = designTBExternalPaccoutMapper.selectOne(queryWrapper);
        if(one == null){
            throw new ServiceException("当前基础数据库系统没有已绑定的账号！");
        }
        return one;
    }

    /**
     * 登录基因库
     */
    private JSONObject login(){
        DesignTBExternalPaccout one = getAccount();
        JSONObject loginResponse = login(one.getExternalSystemUserName(), one.getExternalSystemPassword());
        if(loginResponse.getInteger("code")== HttpStatus.OK.value()){
            //存到redis
            String verifyKey = "gene_pool_token:" + SecurityUtils.getUserId();
            redisService.setCacheObject(verifyKey, loginResponse, 12*60L, TimeUnit.MINUTES);
        }else{
            throw new ServiceException(loginResponse.getString("msg"));
        }
        return loginResponse;
    }

    /**
     * 创建消息头
     * @return
     */
    private HttpHeaders getHeaders(String authorization,String authorizationKey) {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.set("Authorization",authorization);
        httpHeaders.set("Authorization-Key",authorizationKey);
        return httpHeaders;
    }

    /**
     * 验证是否登陆
     * @return
     */
    private HttpHeaders isLogin(){
        String verifyKey = "gene_pool_token:" + SecurityUtils.getUserId();
        JSONObject token = redisService.getCacheObject(verifyKey);
        String authorization ;
        String authorizationKey ;
        if(token == null){
            JSONObject loginResponse = login();
            authorization = loginResponse.getString("token");
            authorizationKey = loginResponse.getString("tokenKey");
        }else{
            authorization = token.getString("token");
            authorizationKey = token.getString("tokenKey");
        }
        return getHeaders(authorization, authorizationKey);
    }


    /**
     * 获取列表
     * @param assembly_accession
     * @return
     */
    public List<GeneDataVo> geneList(String assembly_accession){
        HttpHeaders headers = isLogin();
        JSONObject body = restTemplate.exchange(
                url+"api/ardb/get_genome_data?assembly_accession="+assembly_accession,
                HttpMethod.GET,
                new HttpEntity<>(null,headers),
                JSONObject.class
        ).getBody();
        //请求失败处理
        if(!body.getBoolean("success")){
            //Token失效相关
            if(
                    body.getInteger("code") == HttpStatus.UNAUTHORIZED.value() ||
                            (body.getInteger("code") == HttpStatus.BAD_GATEWAY.value() && "Token 过期".equals(body.getString("msg")))
            ){
                String verifyKey = "gene_pool_retry_flag:" + SecurityUtils.getUserId();
                //当返回登录超后一个小时内是否重试过
                Boolean retryFlag = redisService.getCacheObject(verifyKey);
                if(retryFlag==null){
                    redisService.setCacheObject(verifyKey, true, 60L, TimeUnit.MINUTES);
                    login();
                    log.info("已重新获取Token");
                    return geneList(assembly_accession);
                }
            }
            if(body.getInteger("code") != HttpStatus.OK.value()){
                String errMsg = "【基础数据库返回错误】: "+body.getString("msg");
                log.error(errMsg);
                throw new ServiceException(errMsg);
            }
        }
        JSONArray geneData = body.getJSONArray("data");
        List<GeneDataVo> list = new ArrayList<>();
        if(geneData != null){
            for (int i = 0; i < geneData.size(); i++) {
                GeneDataVo geneDataVo = new GeneDataVo();
                JSONObject sondata =  geneData.getJSONObject(i);
                geneDataVo.setSeqRelDate(sondata.getString("seqRelDate"));
                geneDataVo.setSubLibrary(sondata.getString("subLibrary"));
                geneDataVo.setGeneName(getGeneName(sondata.getString("organismName")));
                list.add(geneDataVo);
                JSONArray geneFiles = sondata.getJSONArray("ftpPathList");
                List<GeneDataFileVo> geneDataFileVoList = getGeneDataFileVo(geneFiles);
                geneDataVo.setGeneDataFileVoList(geneDataFileVoList);
            }
        }
        return list;
    }

    public String getGeneName(String path) {
        return path.substring(path.lastIndexOf("/") + 1);
    }

    public List<GeneDataFileVo> getGeneDataFileVo(JSONArray geneFiles) {
        List<GeneDataFileVo> geneDataFileVoList = new ArrayList<>();
        for (int i = 0; i < geneFiles.size(); i++) {
            GeneDataFileVo geneDataFileVo = new GeneDataFileVo();
            geneDataFileVo.setPath(geneFiles.getString(i));
            geneDataFileVo.setName(getGeneName(geneFiles.getString(i)));
            geneDataFileVoList.add(geneDataFileVo);
        }
        return geneDataFileVoList;
    }

    /**
     * 下载基因库文件到本地
     * @param files
     * @param programmeId
     */
    @Transactional
    public void geneFiles(String files, String programmeId) {
        String separator = File.separator;
        //本地文件夹
        String localPath = sequencePath + separator + programmeId ;
        //临时文件夹
        String localPathTemp = sequencePath + separator + programmeId + "_temp";
        FileUtil.clean(new File(localPathTemp));
        JSONArray filesArray = JSONArray.parseArray(files);
        //方案文件名
        String[] sourceFiles = new String[filesArray.size()];
        //下载并解压文件
        for (int i = 0; i < filesArray.size(); i++) {
            JSONObject file = filesArray.getJSONObject(i);
            String name = file.getString("name");
            if(!name.endsWith(".gz")){
                throw new ServiceException("文件不合法!");
            }
            String path = file.getString("path");
            File localFile = new File(localPathTemp + separator + name);
            if(localFile.exists()){
                FileUtil.del(localFile);
            }
            HttpUtil.downloadFile(path, localFile);
            String mainName = FileUtil.mainName(localFile);

            byte[] bytes = null;
            //解压gz
            try{
                bytes = ZipUtil.unGzip(FileUtil.readBytes(localFile));
            }catch (IORuntimeException e){
                FileUtil.del(localFile);
                throw new ServiceException(name + "  文件损坏或不完整!");
            }

            //拼接本地文件名
            sourceFiles[i]=mainName;

            File unGzipFile = FileUtil.writeBytes(bytes, new File(localPathTemp + separator + mainName));
            System.err.println(unGzipFile.getPath() + ": " + unGzipFile.length());
            //删除压缩包
            FileUtil.del(localFile);
        }
        //删除本地文件夹
        FileUtil.del(new File(localPath));
        //临时文件夹改名为本地文件夹
        new File(localPathTemp).renameTo(new File(localPath));
        System.err.println(Arrays.toString(sourceFiles));
        try {
            sequenceService.cutSequenceFile(sourceFiles, programmeId);
        } catch (IOException e) {
            throw new ServiceException("切分序列失败!");
        }
    }

    //解压gz
    private void unGzip(String path,String dest){
        File file = new File(path);
        byte[] bytes = ZipUtil.unGzip(FileUtil.readBytes(file));
        File unGzipFile = FileUtil.writeBytes(bytes, dest);
    }


}
